Me topé con un hecho sorprendente (para mí).
console.log("asdf".replace(/.*/g, "x"));¿Por qué dos reemplazos? Parece que cualquier cadena no vacía sin líneas nuevas producirá exactamente dos reemplazos para este patrón. Usando una función de reemplazo, puedo ver que el primer reemplazo es para toda la cadena y el segundo es para una cadena vacía.
Según el estándar ECMA-262 , String.prototype.replace llama a RegExp.prototype[@@replace] , que dice:
11. Repeat, while done is false a. Let result be ? RegExpExec(rx, S). b. If result is null, set done to true. c. Else result is not null, i. Append result to the end of results. ii. If global is false, set done to true. iii. Else, 1. Let matchStr be ? ToString(? Get(result, "0")). 2. If matchStr is the empty String, then a. Let thisIndex be ? ToLength(? Get(rx, "lastIndex")). b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). c. Perform ? Set(rx, "lastIndex", nextIndex, true). donde rx es /.*/g y S es 'asdf' .
Ver 11.c.iii.2.b:
B. Deje que nextIndex sea AdvanceStringIndex(S, thisIndex, fullUnicode).
Por lo tanto, en 'asdf'.replace(/.*/g, 'x') en realidad es:
[] , lastIndex = 0'asdf' , resultados = [ 'asdf' ] , lastIndex = 4'' , resultados = [ 'asdf', '' ] , lastIndex = 4 , AdvanceStringIndex , establezca lastIndex en 5null , resultados = [ 'asdf', '' ] , retornoPor lo tanto hay 2 coincidencias.
La primera coincidencia es obviamente "asdf" (Posición [0,4]). Debido a que la bandera global ( g ) está establecida, continúa buscando. En este punto (Posición 4), encuentra una segunda coincidencia, una cadena vacía (Posición [4,4]).
Recuerda que * coincide con cero o más elementos.